//
// srm.cpp
// srm
// System Reference Manual (SRM) Generator / Generates SRM from special source code comments (//@)
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

// srm - Create a System reference manual based on //@ comments in .h and .cpp files 
//       in a specified directory

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#include <iostream>
#include <fstream>
#include "../defines.h"
#include "../entry.h"
#include "../SDK/ToolsInterface.h"

using namespace std;
#include <xercesc/util/PlatformUtils.hpp>

#define MAX_LINE 4096

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

struct DefineStackItem {
	char* name;
	bool include;
	bool last_active;
	DefineStackItem* next;
};

struct DefineItem {
	char* name;
	DefineItem* next;
};

class DefinesStack {
public:
	void DefineStack() {
		items = NULL;
		defines = NULL;
		active = false;
	};

	void IfDef( char* name ) {
//		printf( "IFDEF: %s\n", name );

		DefineStackItem* newitem = new DefineStackItem;
		newitem->last_active = active;
		newitem->include = true;
		newitem->name = strdup( name );

		newitem->next = items;
		items = newitem;

		SetActive();
	};

	void IfNDef( char* name ) {
//		printf( "IFNDEF: %s\n", name );

		DefineStackItem* newitem = new DefineStackItem;
		newitem->name = strdup( name );
		newitem->last_active = active;
		newitem->next = items;
		newitem->include = false;
		items = newitem;

		SetActive();
	};

	void EndIf() {
//		printf( "ENDIF\n" );
		assert( items );

		DefineStackItem* pop = items;
		items = items->next;

		if (items)
			active = items->last_active;
		else
			active = true;

		free( pop->name );
		delete pop;
	};

	void Else() {
//		printf( "ELSE\n" );

		assert( items );
		items->include = !items->include;

		SetActive();
	};

	void AddDefine( char* name ) {
		DefineItem* temp = new DefineItem;
		temp->name = strdup( name );
		temp->next = defines;
		defines = temp;
	};

	bool active;

private:
	DefineStackItem* items;
	DefineItem* defines;

	bool IsDefined( char* name ) {
		DefineItem* temp = defines;
		while (temp) {
			if (strcmp( temp->name, name ) == 0)
				return true;
			temp = temp->next;
		};
		return false;
	};

	bool SetActive() {
		// Set the active flag to the previous levels active flag
		DefineStackItem* previous = items->next;
		if (previous)
			active = previous->last_active;
		else
			active = true;

		if (items->include)
			active = active & (IsDefined( items->name ));
		else
			active = active & (!IsDefined( items->name ));
		return active;
	};
};
DefinesStack stack;

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


char* currdir;
char* currfile;
char* currext;
char* openingdir;
char* devfile;				// current source code file
bool devcomments = true;	// flag for development comments
bool firstdev = false;
bool lastdev = false;

class dbtable
{
public:
	dbtable();
	dbtable( const char* tbl, const char* iotype, const char* access, const char* linkformat, ... );
	~dbtable();

	char* table;
	char* link;
	char* iotype;
	char* access;
	dbtable* next;
};

dbtable::dbtable()
{
	table = NULL;
	link = NULL;
	iotype = NULL;
	access = NULL;
	next = NULL;
};

dbtable::dbtable( const char* tbl, const char* type, const char* acc, const char* linkformat, ... ) {
	// Fill in the table
	if (tbl) {
		table = new char[ strlen( tbl ) + 1 ];
		strcpy( table, tbl );
	} else
		table = NULL;

	// Fill in IO type
	if (type) {
		iotype = new char[ strlen( type ) + 1 ];
		strcpy( iotype, type );
	} else
		iotype = NULL;

	if (acc) {
		access = new char[ strlen( acc ) + 1 ];
		strcpy( access, acc );
	} else
		access = NULL;

	// Fill in the link
	if (linkformat) {
        va_list args;
        va_start(args, linkformat);

		link = new char[ 1024 ];
		vsnprintf( link, 1024, linkformat, args );
		va_end(args);
 	} else
		link = NULL;

	next = NULL;
};

dbtable::~dbtable()
{
	if ( table )
		delete [] table;
	if ( link )
		delete [] link;
	if ( iotype )
		delete [] iotype;
	if ( access )
		delete [] access;
};

// chapter object, one exists for each chapter in the document
class chapter
{
public:
	chapter();
	~chapter();

	void AddDetail( const char* value, ... ) {
		const long TEMP_SIZE = 2048;
		int size;
		char* temp = new char[ TEMP_SIZE ];

        va_list args;
        va_start(args, value);
		size = vsnprintf( temp, TEMP_SIZE, value, args );
		va_end(args);

		if (!detail) {
			detail = temp;
			temp = NULL;
		} else {
			char* new_string = new char[ 1 + strlen( detail ) + size ];
			sprintf( new_string, "%s%s", detail, temp );
			delete [] detail;
			detail = new_string;
		};

		if (temp)
			delete [] temp;
	};

	long section;
	char* name;
	char* sourcefile;
	char* code;
	long number;
	char* critical;
	char* invalid;
	char* general;
	char* detail;
	dbtable* tablelist;
	dbtable* sublist;
	dbtable* bufferlist;
	dbtable* errorlist;
	char* inputfile;
	char* outputfile;
	char* dbf;
	char* sysvals;
	char* dxf;
	char* vars;
	char* devnotes;
	dbtable* parlist;
	char* uex;
	dbtable* utillist;
	chapter* next;

	int detailsize;
};

// chapter class constructor
chapter::chapter()
{
	section = 0;
	name = NULL;
	sourcefile = NULL;
	code = NULL;
	number = 0;
	critical = NULL;
	invalid = NULL;
	general = NULL;
	detail = NULL;
	tablelist = NULL;
	sublist = NULL;
	bufferlist = NULL;
	errorlist = NULL;
	inputfile = NULL;
	outputfile = NULL;
	dbf = NULL;
	sysvals = NULL;
	dxf = NULL;
	vars = NULL;
	devnotes = NULL;
	parlist = NULL;
	uex = NULL;
	utillist = NULL;
	next = NULL;
};

// chapter class destructor
chapter::~chapter()
{
	if ( detail )
		delete[] detail;
	if ( name )
		delete[] name;
	if ( sourcefile )
		delete[] sourcefile;
	if ( code )
		delete[] code;
	if ( critical )
		delete[] critical;
	if ( invalid )
		delete[] invalid;
	if ( general )
		delete[] general;
	if ( inputfile )
		delete[] inputfile;
	if ( outputfile )
		delete[] outputfile;
	if ( dbf )
		delete[] dbf;
	if ( sysvals )
		delete[] sysvals;
	if ( dxf )
		delete[] dxf;
	if ( vars )
		delete[] vars;
	if ( devnotes )
		delete[] devnotes;
	if ( uex )
		delete[] uex;
	if ( tablelist )
	{
		dbtable* junk;
		dbtable* temp = tablelist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
	if ( sublist )
	{
		dbtable* junk;
		dbtable* temp = sublist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
	if ( errorlist )
	{
		dbtable* junk;
		dbtable* temp = errorlist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
	if ( bufferlist )
	{
		dbtable* junk;
		dbtable* temp = bufferlist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
	if ( parlist )
	{
		dbtable* junk;
		dbtable* temp = parlist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
	if ( utillist )
	{
		dbtable* junk;
		dbtable* temp = utillist;
		while ( temp )
		{
			junk = temp;
			temp = temp->next;
			delete junk;
		};
	};
};

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

// Change a substring
//
// Replaces a substring in a string with another string, used to generate escape sequences for HTML
char* Replace( char* text, char* oldstr, char* newstr )
{
	char* temp;
	char* found;
	char* retval = text;
	long loc;

	found = strstr( retval, oldstr );
	while ( found )
	{
		temp = new char[ strlen( retval ) + strlen( newstr ) + 2 ];
		memset( temp, 0, strlen( retval ) + strlen( newstr ) + 2 );
		memcpy( temp, retval, strlen( retval ) );
		loc = found - retval;
		memcpy( temp, retval, loc );
		memcpy( temp + loc, newstr, strlen( newstr ) );
		memcpy( temp + loc + strlen( newstr ), found + 1, strlen( found ) + 1 );
		delete[] retval;
		retval = temp;
		found = strstr( retval + loc + strlen( newstr ), oldstr );
	};

	return retval;
};




// Fix for HTML
//
// Makes certain characters into HTML escape sequences (so it'll look Nice!)
char* FixNice( char* text )
{
	// First, count required size of new string
	int count = 0;
	int old_count = 0;

	char* temp = text;
	while (*temp) {
		if (*temp == '&')
			count += 5;
		else if (*temp == '<' || *temp == '>')
			count += 4;
		else if (*temp == '\"')
			count += 6;
		else
			count++;
		temp++;
		old_count++;
	};

	// Now that we have replacement size, reallocate and do stuff
	char* retval = new char[ count + 1 ];
	char* result = retval;
	temp = text;
	while (*temp) {
		if (*temp == '&') {
			strcpy( result, "&amp;" );
			result += 5;
		} else if (*temp == '<') {
			strcpy( result, "&lt;" );
			result += 4;
		} else if (*temp == '>') {
			strcpy( result, "&gt;" );
			result += 4;
		} else if (*temp == '\"') {
			strcpy( result, "&quot;" );
			result += 6;
		} else {
			*result = *temp;
			result++;
		};
		temp++;
	};

	// NULL terminate
	*result = 0;

	// Return results;
	return retval;
};



// Trim Leading and Trailing Spaces
//
// Removes whitespace characters from beginning and end of a string.
char* Trim( char* text )
{
	// first trim off the front
	char* f = text;
	while (f[0] == ' ' || f[0] == '\t')
		f++;
    
	// then trim off the end
	char* e = text + strlen( text ) - 1;
	while (e[0] == ' ' || e[0] == '\t')
		e--;

	// create new string, copy and return
	char* retval = NULL;
	if ( e > f )
	{
		retval = new char[ e - f + 2 ];
		memset( retval, 0, e - f + 2 );
		strncpy( retval, f, e - f + 1 );
	};
	return retval;
};

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

// Parse General Document Text
//
// This text exists in the document after the Title and before the Copyright Notice
char* doctext;
void ParseDOC( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( doctext )
	{
		char* temp = new char[ strlen( doctext ) + strlen( loc ) + 5 ];
		sprintf( temp, "%s%s<BR>", doctext, loc );
		delete[] doctext;
		doctext = temp;
	}
	else
	{
		doctext = new char[ strlen( loc ) + 5 ];
		sprintf( doctext, "%s<BR>", loc );
	};
};

// Parse Manual Title Text
//
// This is used to create the Manual Title Page
char* doctitle;
char* mantext;
void ParseMAN( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( mantext )
	{
		char* temp = new char[ strlen( mantext ) + strlen( loc ) + 26 ];
		sprintf( temp, "%s<H1><CENTER>%s</CENTER></H1>", mantext, loc );
		delete[] mantext;
		mantext = temp;
	}
	else
	{
		doctitle = new char[ strlen( loc ) + 25 ];
		sprintf( doctitle, "%s SYSTEM REFERENCE MANUAL", loc );
		mantext = new char[ strlen( loc ) + 98 ];
		sprintf( mantext, "<A NAME='ManualTitle'><H1><CENTER>%s</CENTER></H1><H1><CENTER>SYSTEM REFERENCE MANUAL</CENTER></H1>", loc );
	};
};

// Parse Copyright Notice Text
//
// This is used to create a Copyright notice, located after Document Text and before the TOC
char* coptext;
void ParseCOP( char* loc )
{
	if ( *loc == 0 )
	{
		if ( coptext )
		{
			char* temp = new char[ strlen( coptext ) + 5 ];
			sprintf( temp, "%s<BR>", coptext );
			delete[] coptext;
			coptext = temp;
		};
		return;
	};
	if ( coptext )
	{
		char* temp = new char[ strlen( coptext ) + strlen( loc ) + 5 ];
		sprintf( temp, "%s%s<BR>", coptext, loc );
		delete[] coptext;
		coptext = temp;
	}
	else
	{
		coptext = new char[ strlen( loc ) + 25 ];
		sprintf( coptext, "<A NAME='Copyright'>%s<BR>", loc );
	};
};

// Parse Section Title Text
//
// This creates sections in the document and assigns titles to each of them.
// Generally, the following is true:
// SCT 0 Developer Notes ( only if devcomments = true )
// SCT 1 Database Definitions
// SCT 2 Transaction Descriptions
// SCT 3 Subtransaction Descriptions
// SCT 4 Batch Job Transaction Descriptions
// SCT 5 Utility Transaction Descriptions
char** sectitles;
char** sectoc;
char** secname;
long sectitlescount;
void ParseSCT( char* loc )
{
 	if ( *loc == 0 )
		return;
	char** temptitles;
	char** temptoc;
	char** tempname;

	// get section number
	int len = strspn( loc, "0123456789" );
	char* number = new char[ len + 1 ];
	memset( number, 0, len + 1 );
	strncpy( number, loc, len );
	loc += len;

	if ( sectitles )
	{
		int i;
		// expand titles
		temptitles = new char*[ sectitlescount + 1 ];
		for( i = 0; i < sectitlescount; i++ )
			temptitles[ i ] = sectitles[ i ];
		delete[] sectitles;
		sectitles = temptitles;

		// expand toc
		temptoc = new char*[ sectitlescount + 1 ];
		for ( i = 0; i < sectitlescount; i++ )
			temptoc[ i ] = sectoc[ i ];
		delete[] sectoc;
		sectoc = temptoc;

		// expand name
		tempname = new char*[ sectitlescount + 1 ];
		for ( i = 0; i < sectitlescount; i++ )
			tempname[ i ] = secname[ i ];
		delete[] secname;
		secname = tempname;

		// create new title
		char* temp = new char[ strlen( number ) * 2 + strlen( loc ) + 67 ];
		sprintf( temp, "<HR SIZE=5><A NAME='Section_%s'><H2 ALIGN=RIGHT>%s</H2><H2>%s</H2>", number, number, loc );
		sectitles[ sectitlescount ] = temp;

		// create new toc
		temp = new char[ strlen( currfile ) + strlen( number ) * 3 + strlen( currext ) + strlen( loc ) + 42 ];
		sprintf( temp, "<A HREF='%s_%s.%s#Section_%s'>%s - %s</A>", currfile, number, currext, number, number, loc );
		sectoc[ sectitlescount ] = temp;

		// create new name
		temp = new char[ strlen( loc ) + 1 ];
		sprintf( temp, "%s", loc );
		secname[ sectitlescount ] = temp;
		sectitlescount++;
	}
	else
	{
		sectitlescount = 1;
		sectitles = new char*[ 1 ];
		sectoc = new char*[ 1 ];
		secname = new char*[ 1 ];

		// create new title
		char* temp = new char[ strlen( number ) * 2 + strlen( loc ) + 67 ];
		sprintf( temp, "<HR SIZE=5><A NAME='Section_%s'><H2 ALIGN=RIGHT>%s</H2><H2>%s</H2>", number, number, loc );
		sectitles[ 0 ] = temp;

		// create new toc
		temp = new char[ strlen( currfile ) + strlen( number ) * 3 + strlen( currext ) + strlen( loc ) + 42 ];
		sprintf( temp, "<A HREF='%s_%s.%s#Section_%s'>%s - %s</A>", currfile, number, currext, number, number, loc );
		sectoc[ 0 ] = temp;

		// create new name
		temp = new char[ strlen( loc ) + 1 ];
		sprintf( temp, "%s", loc );
		secname[ 0 ] = temp;
	};
};

// Parse Current Section Assignment
//
// Sets the current section for the current chapter and chapter text
long currentsection;
void ParseSEC( char* loc )
{
	if ( *loc == 0 )
		return;
	// get section number
	long tempsection = atol( loc );
	if ( tempsection )
		currentsection = tempsection;
};

// Parse Chapter Title Text
//
// Inserts the chapter title text in alphabetical order into a list of all chapters in the document
chapter* chapterlist;
chapter* currentchapter;
void ParseCHA( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( chapterlist )
	{
		// insertion sort
		chapter* tempchap = chapterlist;
		chapter* lastchap = NULL;
		chapter* newchap = new chapter;
		if ( currentsection != 1 )
			newchap->AddDetail( "<H4>DETAILED DESCRIPTION</H4>" );

		bool inserted = false;
		int compare = 0;
		while ( tempchap && !inserted )
		{
			compare = strcmp( loc, tempchap->name );
			// current node is greater than new chapter, insert here!
			if ( compare == 0 )
			{
				cout << "Chapter " << loc << " already exists.  Aborting" << endl;
				exit(1);
			};
			if ( compare < 0 )
			{
				if ( lastchap )
				{
					// insert into middle of list
					newchap->next = tempchap;
					lastchap->next = newchap;
				}
				else
				{
					// inserting at start of list
					newchap->next = chapterlist;
					chapterlist = newchap;
				};
				
				// insert information
				newchap->section = currentsection;
				newchap->name = new char[ strlen( loc ) + 1 ];
				sprintf( newchap->name, "%s", loc );
				if ( currentsection == 1 )
				{
					char* trimloc = Trim( loc );
					newchap->code = new char[ strlen( trimloc ) + 1 ];
					sprintf( newchap->code, "%s", trimloc );
					delete[] trimloc;
				}
				else
				{
					if ( devcomments )
					{
                        newchap->sourcefile = new char[ strlen( devfile ) + 1 ];
						sprintf( newchap->sourcefile, "%s", devfile );
					};
				};
				inserted = true;
			};
			lastchap = tempchap;
			tempchap = tempchap->next;
		}
		if ( !inserted )
		{
			// insert at end of list
			lastchap->next = newchap;
			newchap->section = currentsection;
			newchap->name = new char[ strlen( loc ) + 1 ];
			sprintf( newchap->name, "%s", loc );
			if ( currentsection == 1 )
			{
				char* trimloc = Trim( loc );
				newchap->code = new char[ strlen( trimloc ) + 1 ];
				sprintf( newchap->code, "%s", trimloc );
				delete[] trimloc;
			}
			else
			{
				if ( devcomments )
				{
					newchap->sourcefile = new char[ strlen( devfile ) + 1 ];
					sprintf( newchap->sourcefile, "%s", devfile );
				};
			};
		}
		currentchapter = newchap;
	}
	else
	{
		chapterlist = new chapter;
		chapterlist->section = currentsection;
		if ( currentsection != 1 )
		{
			chapterlist->AddDetail( "<H4>DETAILED DESCRIPTION</H4>" );
		};
		chapterlist->name = new char[ strlen( loc ) + 1 ];
		sprintf( chapterlist->name, "%s", loc );
		if ( currentsection == 1 )
		{
			char* trimloc = Trim( loc );
			chapterlist->code = new char[ strlen( trimloc ) + 1 ];
			sprintf( chapterlist->code, "%s", trimloc );
			delete[] trimloc;
		}
		else
		{
			if ( devcomments )
			{
				chapterlist->sourcefile = new char[ strlen( devfile ) + 1 ];
				sprintf( chapterlist->sourcefile, "%s", devfile );
			};
		};
		currentchapter = chapterlist;
	};
};

// Parse the Transaction Code Text
//
// Assigns a transaction code to the current chapter
void ParseCOD( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( !(currentchapter->code) )
	{
		char* trimloc = Trim( loc );
		currentchapter->code = new char[ strlen( trimloc ) + 1 ];
		sprintf( currentchapter->code, "%s", trimloc );
		delete[] trimloc;
	}
	else
	{
		cerr << "ERROR : Attempt to replace TXN Code for " << currentchapter->name << " chapter failed" << endl;
	};
};

// Parse the Transaction Number Text
//
// Assigns a transaction number to the current chapter
void ParseNUM( char* loc )
{
	if ( *loc == 0 )
		return;
	long tempnum = atol( loc );
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( !currentchapter->number && tempnum )
	{
		currentchapter->number = tempnum;
	}
	else
	{
		if ( loc[0] != 'n' && loc[1] != '/' && loc[2] != 'a' )
			cerr << "ERROR : Attempt to replace or assign TXN Number for " << currentchapter->name << " chapter failed" << endl;
	};
};

// Parse the Critical Fields Text
//
// Creates or Adds to a list of critical fields for the current chapter
void ParseCRI( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->critical )
	{
		char* temp = new char[ strlen( currentchapter->critical ) + strlen( loc ) + 3 ];
		sprintf( temp, "%s, %s", currentchapter->critical, loc );
		delete[] currentchapter->critical;
		currentchapter->critical = temp;
	}
	else
	{
		currentchapter->critical = new char[ strlen( loc ) + 23 ];
		sprintf( currentchapter->critical, "<TT>Critical Fields : %s", loc );
	};
};

// Parse the Invalid Fields Text
//
// Creates or Adds to a list of invalid fields for the current chapter
void ParseINV( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->invalid )
	{
		char* temp = new char[ strlen( currentchapter->invalid ) + strlen( loc ) + 3 ];
		sprintf( temp, "%s, %s", currentchapter->invalid, loc );
		delete[] currentchapter->invalid;
		currentchapter->invalid = temp;
	}
	else
	{
		currentchapter->invalid = new char[ strlen( loc ) + 22 ];
		sprintf( currentchapter->invalid, "<TT>Invalid Fields : %s", loc );
	};
};

// Parse the General Description Text
//
// Used to show a general description for the current chapter
void ParseGEN( char* loc )
{
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->general )
	{
		char* temp = new char[ strlen( currentchapter->general ) + strlen( loc ) + 6 ];
		sprintf( temp, "%s %s<BR>", currentchapter->general, loc );
		delete[] currentchapter->general;
		currentchapter->general = temp;
	}
	else
	{
		currentchapter->general = new char[ strlen( loc ) + 33 ];
		sprintf( currentchapter->general, "<H4>GENERAL DESCRIPTION</H4>%s<BR>", loc );
	};
};

// Parse the Table List Text
//
// Used to list all database tables used in the current chapter
void ParseTAB( char* loc )
{
	if ( *loc == 0 )
		return;

	// get tablename, access type, and I/O type text
	char* loctok = new char[ strlen( loc ) + 1];
	sprintf( loctok, "%s", loc );
	char seps[] = " \t\n";
	char empty[] = " ";
	char* tbl = empty;
	char* iotype = empty;
	char* access = empty;
	tbl = strtok( loctok, seps );
	if ( tbl )
		access = strtok( NULL, seps );
	else
		tbl = empty;
	if ( access )
		iotype = strtok( NULL, seps );
	else
		access = empty;
	if ( !iotype )
		iotype = empty;

	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};


	// Create the record
	dbtable* newtable = new dbtable(
		tbl,
		iotype,
		access, 
		"<A HREF='%s_1.%s#Chapter_%s'>%s</A>", currfile, currext, tbl, tbl );

	// Find where it goes
	if ( currentchapter->tablelist )
	{
		dbtable* temptable = currentchapter->tablelist;
		while ( temptable )
		{
			// add tablename to list here...
			if ( !temptable->next ) {
				temptable->next = newtable;
				temptable = NULL;
			} else
				temptable = temptable->next;
		};
	}
	else
		currentchapter->tablelist = newtable;
};




// Parse the Subtransaction Text
//
// Used to list all of the subtransactions used in the current chapter
void ParseSUB( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// Get sub code
	char seps[] = " \n";
	char* dupstring = strdup( loc );
	char* token;
	char* sname;
	char* punc = new char[ 2 ];
	strcpy( punc, " " );
	token = strtok( dupstring, seps );
	sname = new char[ strlen( token ) + 1 ];
	strcpy( sname, token );
	loc = loc + strlen( token ) + 1;

	// skip past parameters
	bool skip = false;
	char* parm = NULL;
	if ( sname && sname[ strlen( sname ) - 1 ] == ')' )
	{
		parm = strdup( strrchr( sname, '(' ) );
		sname[ strlen( sname ) - strlen( parm ) ] = 0;
		skip = true;
	};

	// Handle punctuation
	if ( sname && sname[ strlen( sname ) - 2 ] == ')' )
	{
		parm = strdup( strrchr( sname, '(' ) );
		parm[ strlen( parm ) - 1 ] = 0;
		punc[0] = sname[ strlen( sname ) - 1 ];
		sname[ strlen( sname ) - strlen( parm ) - 1 ] = 0;
		skip = true;
	};

	// Create the new record
	dbtable* newsub = new dbtable(
		sname,
		NULL,
		NULL,
		"<TT><A HREF='%s_3.%s#Chapter_%s'>%s</A></TT><BR>", currfile, currext, sname, sname );

	// Add to ordered sub list
	if ( currentchapter->sublist )
	{
		dbtable* tempsub = currentchapter->sublist;
		bool found = false;
		dbtable* last = NULL;
		while ( !found && tempsub )
		{
			if ( XCStrCaseCompare( tempsub->table, sname ) > 0 )
			{
				if ( last )
				{
					newsub->next = last->next;
					last->next = newsub;
				}
				else
				{
					newsub->next = currentchapter->sublist;
					currentchapter->sublist = newsub;
				};
				found = true;
			}
			if ( XCStrCaseCompare( tempsub->table, sname ) == 0 )
			{
				delete newsub;
				found = true;
			};
			last = tempsub;
			tempsub = tempsub->next;
		};
		if ( !found )
			last->next = newsub;
	}
	else
	{
		currentchapter->sublist = newsub;
	};

	// add to detail text
	if (skip)
		currentchapter->AddDetail( "<LI>Call <A HREF='%s_3.%s#Chapter_%s'>%s</A>%s Subtransaction%s %s<BR>", currfile, currext, sname, sname, parm, punc, loc );
	else
		currentchapter->AddDetail( "<LI>Call <A HREF='%s_3.%s#Chapter_%s'>%s</A> Subtransaction %s<BR>", currfile, currext, sname, sname, loc );

	free( dupstring );
	delete[] sname;
	delete[] punc;
	if ( parm )
		free( parm );
};



// Parse the Buffer List Text
//
// Used to list all of the buffers associated with the current chapter
void ParseBUF( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// get buffer name and i/o type
	char* loctok = new char[ strlen( loc ) + 2];
	memset( loctok, 0, strlen( loc ) + 2 );
	sprintf( loctok, "%s", loc );
	char seps[] = " \t\n";
	char* buf;
	char* iotype;
	buf = strtok( loctok, seps );
	if ( buf )
		iotype = strtok( NULL, seps );
	else
		cerr << "ERROR : Incomplete BUF tag for " << currentchapter->name << endl;
	if ( !iotype )
		cerr << "ERROR : BUF tag " << buf << " missing I/O Type for " << currentchapter->name << endl;

	// Create the new record
	dbtable* newbuf = new dbtable(
		buf,
		iotype,
		NULL, 
		"<A HREF='%s_6.%s#Chapter_%s'>%s</A>", currfile, currext, buf, buf );

	// Determine where to add it
	if ( currentchapter->bufferlist )
	{
		dbtable* tempbuf = currentchapter->bufferlist;
		while ( tempbuf )
		{
			if ( !tempbuf->next )
			{
				// add buffer to list here...
				tempbuf->next = newbuf;
				tempbuf = tempbuf->next;
			};
			tempbuf = tempbuf->next;
		};
	}
	else
		currentchapter->bufferlist = newbuf;
};



// Parse the Error List Text
//
// Used to list all of the errors associated with the current chapter
void ParseERR( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// get error type and text
	char* loctok = new char[ strlen( loc ) + 2];
	memset( loctok, 0, strlen( loc ) + 2 );
	sprintf( loctok, "%s", loc );
	char seps[] = " \t\n";
	char* type;
	type = strtok( loctok, seps );
	loc = loc + strlen( type ) + 1;
	char* fulltype = new char[ 9 ];
	switch( type[0] )
	{
	case 'C':
		strcpy( fulltype, "CRITICAL" );
		break;
	case 'E':
		strcpy( fulltype, "ERROR" );
		break;
	case 'W':
		strcpy( fulltype, "WARNING" );
		break;
	case 'S':
		strcpy( fulltype, "STATUS" );
		break;
	default:
		cerr << "ERROR : Invalid ERROR type " << type[0] << " in " << currentchapter->name << endl;
		strcpy( fulltype, "-INVALID" );
		break;
	};

	// Create the error record
	dbtable* newerr = new dbtable( NULL, fulltype, NULL, loc );

	// Add to error list
	if ( currentchapter->errorlist )
	{
		// find insert point
		bool found = false;
		dbtable* insert = currentchapter->errorlist;
		dbtable* last = NULL;
		while ( insert && !found )
		{
			if ( strcmp( loc, insert->link ) < 0 )
			{
				if ( last )
				{
					last->next = newerr;
					newerr->next = insert;
				}
				else
				{
					newerr->next = currentchapter->errorlist;
					currentchapter->errorlist = newerr;
				};
				found = true;
			};
			last = insert;
			insert = insert->next;
		};
		if ( !found )
			last->next = newerr;
	}
	else
		currentchapter->errorlist = newerr;

	// add to detail text
	char* chaptype = NULL;
	switch( currentsection )
	{
	case 2:
		chaptype = strdup( "Transaction." );
		break;
	case 3:
		chaptype = strdup( "Subtransaction." );
		break;
	case 4:
		chaptype = strdup( "Batch Job." );
		break;
	case 5:
		chaptype = strdup( "Utility." );
		break;
	default:
		chaptype = strdup( "." );
		break;
	};


	if ( type[0] == 'C' )
		currentchapter->AddDetail( "<P><B>Raise Error (%s)</B>: <TT>%s</TT> <B>and EXIT</B></P>", fulltype, loc );
	else
		currentchapter->AddDetail( "<P><B>Raise Error (%s)</B>: <TT>%s</TT></P>", fulltype, loc );

	delete[] loctok;
	delete[] fulltype;
	delete[] chaptype;
};



// Parse the Input File List
// 
// Used to list all of the input files for the current chapter
void ParseINP( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->inputfile )
	{
		char* temp = new char[ strlen( currentchapter->inputfile ) + strlen( loc ) + 14 ];
		sprintf( temp, "%s<TT>%s</TT><BR>", currentchapter->inputfile, loc );
		delete[] currentchapter->inputfile;
		currentchapter->inputfile = temp;
	}
	else
	{
		currentchapter->inputfile = new char[ strlen( loc ) + 67 ];
		sprintf( currentchapter->inputfile, "<TT>Input Files : </TT><BR><TT>-------------</TT><BR><TT>%s</TT><BR>", loc );
	};
};

// Parse the Output File List
//
// Used to list all of the output files for the current chapter
void ParseOUT( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->outputfile )
	{
		char* temp = new char[ strlen( currentchapter->outputfile ) + strlen( loc ) + 14 ];
		sprintf( temp, "%s<TT>%s</TT><BR>", currentchapter->outputfile, loc );
		delete[] currentchapter->outputfile;
		currentchapter->outputfile = temp;
	}
	else
	{
		currentchapter->outputfile = new char[ strlen( loc ) + 69 ];
		sprintf( currentchapter->outputfile, "<TT>Output Files : </TT><BR><TT>--------------</TT><BR><TT>%s</TT><BR>", loc );
	};
};

// Parse the Fixed Detailed Description Text
//
// Used to display a section of the detailed description text with a fixed-width font
// (including fixed spaces)
void ParseFIX( char* loc )
{
	if ( *loc == 0 )
		return;
	char* temploc = new char[ strlen( loc ) + 1 ];
	strcpy( temploc, loc );
	temploc = Replace( temploc, " ", "&nbsp;" );
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	currentchapter->AddDetail( "<TT>%s</TT><BR>", temploc );

	delete[] temploc;
};



// Parse the Non-Bulleted Detailed Description Text
//
// Used to display a non-bulleted line in the detailed description text
void ParseNOB( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	currentchapter->AddDetail( "%s<BR>", loc );
};



// Parse the Group Start Text
//
// Used to start a new group of bulleted items in the detailed description text
void ParseGRS( char* loc )
{
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	if ( *loc != 0 )
		currentchapter->AddDetail( "<B>%s</B><UL>", loc );
	else
		currentchapter->AddDetail( "<UL>" );
};



// Parse the Group End Text
//
// Used to end a group of bulleted items in the detailed description text
void ParseGRE( char* loc )
{
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	if ( *loc != 0 )
		currentchapter->AddDetail( "</UL><B>%s</B>", loc );
	else
		currentchapter->AddDetail( "</UL>" );
};



// Parse the Database Field Text
//
// Used to define a database field
void ParseDBF( char* loc )
{
	if ( *loc == 0 )
		return;
	char seps[] = " \n";
	char* token;
	char* fname;

	// get field name
	token = strtok( loc, seps );
	fname = new char[ strlen( token ) + 1 ];
	strcpy( fname, token );

	// get field type/length
	token = strtok( NULL, seps );

	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->dbf )
	{
		char* temp = new char[ strlen( currentchapter->dbf ) + strlen( fname ) + strlen( token ) + 28 ];
		sprintf( temp, "%s<TR><TD>%s</TD><TD>%s</TD></TR>", currentchapter->dbf, fname, token );
		delete[] currentchapter->dbf;
		currentchapter->dbf = temp;
	}
	else
	{
		currentchapter->dbf = new char[ strlen( fname ) + strlen( token ) + 106 ];
		sprintf( currentchapter->dbf, "<TABLE BORDER=1 WIDTH='75%%'><TR><TH>Field Name</TH><TH>Type (Length)</TH></TR><TR><TD>%s</TD><TD>%s</TD></TR>", fname, token );
	};
	delete[] fname;
};



// Parse Bolded Description Text
//
// Used to enter non-bulleted, bolded items in the detailed description text
void ParseBLD( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	currentchapter->AddDetail( "<B>%s</B><BR>", loc );
};



// Parse System Values Text
//
// Used to enter system value items for each chapter
void ParseSYS( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	if ( currentchapter->sysvals )
	{
		char* temp = new char[ strlen( currentchapter->sysvals ) + strlen( loc ) + 3 ];
		sprintf( temp, "%s, %s", currentchapter->sysvals, loc );
		delete[] currentchapter->sysvals;
		currentchapter->sysvals = temp;
	}
	else
	{
		currentchapter->sysvals = new char[ strlen( loc ) + 21 ];
		sprintf( currentchapter->sysvals, "<TT>System Values : %s", loc );
	};
};



// Parse Data Exchange Format Text
//
// Used to enter data exchange formats for each chapter
void ParseDXF( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	if ( currentchapter->dxf )
	{
		char* temp = new char[ strlen( currentchapter->dxf ) + strlen( loc ) + 3 ];
		sprintf( temp, "%s, %s", currentchapter->dxf, loc );
		delete[] currentchapter->dxf;
		currentchapter->dxf = temp;
	}
	else
	{
		currentchapter->dxf = new char[ strlen( loc ) + 31 ];
		sprintf( currentchapter->dxf, "<TT>Data Exchange Formats : %s", loc );
	};
};



// Parse the Local Variable List Text
//
// Used to list all of the local variables associated with the current chapter
void ParseVAR( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};
	// add to variable list
	if ( currentchapter->vars )
	{
		char* temp = new char[ strlen( currentchapter->vars ) + strlen( loc ) + 14 ];
		sprintf( temp, "%s<TT>%s</TT><BR>", currentchapter->vars, loc );
		delete[] currentchapter->vars;
		currentchapter->vars = temp;
	}
	else
	{
		currentchapter->vars = new char[ strlen( loc ) + 66 ];
		sprintf( currentchapter->vars, "<TT>Local Variables : </TT><BR><TT>--------</TT><BR><TT>%s</TT><BR>", loc );
	};
};



// Parse the Development Notes Text
//
// Used to show development notes in the current chapter
void ParseDEV( char* loc )
{
	if ( !devcomments )
		return;
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// Output the dev comment
	currentchapter->AddDetail( "<FONT color=red>*** %s</FONT><BR>", loc );

	if ( currentchapter->devnotes )
	{
		if ( firstdev )
		{
			char* first = new char[ strlen( currentchapter->devnotes ) + 5 ];
			sprintf( first, "%s<UL>", currentchapter->devnotes );
			delete[] currentchapter->devnotes;
			currentchapter->devnotes = first;
		};
		char* temp = new char[ strlen( currentchapter->devnotes ) + strlen( loc ) + 32 ];
		sprintf( temp, "%s<FONT color=red>*** %s</FONT><BR>", currentchapter->devnotes, loc );
		delete[] currentchapter->devnotes;
		currentchapter->devnotes = temp;
	}
	else
	{
		currentchapter->devnotes = new char[ strlen( loc ) + 36 ];
		sprintf( currentchapter->devnotes, "<UL><FONT color=red>*** %s</FONT><BR>", loc );
	};
	lastdev = true;
};



// Parse Parameters
//
// Used to list all of the parameters associated with the current chapter
void ParsePAR( char* loc )
{
	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// get parameter name and i/o type
	char* loctok = new char[ strlen( loc ) + 2];
	memset( loctok, 0, strlen( loc ) + 2 );
	sprintf( loctok, "%s", loc );
	char seps[] = " \t\n";
	char* par;
	char* iotype;
	par = strtok( loctok, seps );
	if ( par )
		iotype = strtok( NULL, seps );
	else
		cerr << "ERROR : Incomplete PAR tag for " << currentchapter->name << endl;
	if ( !iotype )
		cerr << "ERROR : PAR tag " << par << " missing I/O Type for " << currentchapter->name << endl;

	// Create the record
	dbtable* newpar = new dbtable(
		par,
		iotype,
		NULL,
		"<A HREF='%s_7.%s#Chapter_%s'>%s</A>", currfile, currext, par, par );

	// Add it to the list
	if ( currentchapter->parlist )
	{
		dbtable* temppar = currentchapter->parlist;
		while ( temppar )
		{
			if ( !temppar->next )
			{
				// add buffer to list here...
				temppar->next = newpar;
				temppar = temppar->next;
			};
			temppar = temppar->next;
		};
	}
	else
		currentchapter->parlist = newpar;
};



// Parse the User Exit call text
//
// Used to show user exits in the detailed description text
void ParseUEX( char* loc )
{
	static char uename[ 255 ];

	if ( *loc == 0 )
		return;
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};


	// Determine the user exit name
	char* uefind = strchr( loc, ':' );
	if (uefind) {
		int size = uefind - loc;
		memcpy( uename, loc, size );
		uename[ size ] = 0;
	} else {
		strcpy( uename, "UNKNOWN" );
		cerr << "ERROR : Bad user exit definition" << endl;
	};
	int size = strlen( uename );

	// add to error list
	if ( currentchapter->uex )
	{
		char* temp = new char[ strlen( currentchapter->uex ) + strlen( loc ) + 14 + (size+16) ];
		sprintf( temp, "%s<TT><A HREF='#%s'>%s</A></TT><BR>", currentchapter->uex, uename, loc );
		delete[] currentchapter->uex;
		currentchapter->uex = temp;
	}
	else
	{
		currentchapter->uex = new char[ strlen( loc ) + 68 + (size+16) ];
		sprintf( currentchapter->uex, "<TT>User Exits : </TT><BR><TT>-------------</TT><BR><TT><A HREF='#%s'>%s</A></TT><BR>", uename, loc );
	};

	// Add to the detail text
	currentchapter->AddDetail( "<LI><B><A NAME='%s'>User Exit: <TT>%s</TT></B></A><BR>", uename, loc );
};



// Parse the Utility Call Text
//
// Used to list all of the utilities used in the current chapter
void ParseUTL( char* loc )
{
	// Anything to parse?
	if ( *loc == 0 )
		return;

	// If the chapter is not defined, exit
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// Get utility code
	char seps[] = " \n";
	char* dupstring = strdup( loc );
	char* token;
	char* sname;
	char* punc = new char[ 2 ];

	strcpy( punc, " " );
	token = strtok( dupstring, seps );
	sname = new char[ strlen( token ) + 1 ];
	strcpy( sname, token );
	loc += strlen( token ) + 1;

	// skip past parameters
	bool skip = false;
	char* parm = NULL;
	if ( sname && sname[ strlen( sname ) - 1 ] == ')' )
	{
		parm = strdup( strrchr( sname, '(' ) );
		sname[ strlen( sname ) - strlen( parm ) ] = 0;
		skip = true;
	};

	// handle punctuation
	if ( sname && sname[ strlen( sname ) - 2 ] == ')' )
	{
		parm = strdup( strrchr( sname, '(' ) );
		parm[ strlen( parm ) - 1 ] = 0;
		punc[0] = sname[ strlen( sname ) - 1 ];
		sname[ strlen( sname ) - strlen( parm ) - 1 ] = 0;
		skip = true;
	};

	// Create the new record
	dbtable* newutil = new dbtable(
		sname,
		NULL,
		NULL, 
		"<TT><A HREF='%s_5.%s#Chapter_%s'>%s</A></TT><BR>", currfile, currext, sname, sname );

	// Add to ordered utility list
	if ( currentchapter->utillist )
	{
		dbtable* temputil = currentchapter->utillist;
		bool found = false;
		dbtable* last = NULL;
		while ( !found && temputil )
		{
			if ( XCStrCaseCompare( temputil->table, sname ) > 0 )
			{
				if ( last )
				{
					// Insert in the middle
					newutil->next = last->next;
					last->next = newutil;
				}
				else
				{
					// Add at the front of the list
					newutil->next = currentchapter->utillist;
					currentchapter->utillist = newutil;
				};
				found = true;
			}

			// Found this exact record
			if ( XCStrCaseCompare( temputil->table, sname ) == 0 )
			{
				delete newutil;
				found = true;
			};
			last = temputil;
			temputil = temputil->next;
		};

		// Add at the end if nothing else found
		if ( !found )
			last->next = newutil;
	}
	else
	{
		currentchapter->utillist = newutil;
	};

	// Add to the detail text
	if (skip)
		currentchapter->AddDetail( "<LI>Call <A HREF='%s_5.%s#Chapter_%s'>%s</A>%s Utility%s %s<BR>", currfile, currext, sname, sname, parm, punc, loc );
	else
		currentchapter->AddDetail( "<LI>Call <A HREF='%s_5.%s#Chapter_%s'>%s</A> Utility %s<BR>", currfile, currext, sname, sname, loc );

	// Cleanup
	delete[] sname;
	delete[] punc;
	if ( parm )
		free( parm );
	free( dupstring );
};



// Parse the Detailed Description Text
//
// Used to enter normal, bulleted items in the detailed description text
void Parse( char* loc )
{
	// Nothing to parse
	if ( *loc == 0 )
		return;

	// Ensure that we have this chapter defined
	if ( !currentchapter )
	{
		cerr << "ERROR : No chapter defined" << endl;
		throw;
	};

	// If we already have detail data, add to it
	currentchapter->AddDetail( "<LI>%s<BR>", loc );
};




// Parse a single //@ comment line
void ParseLine( char* line )
{
	// Do we have any data?
	if ( !line )
		return;

	// Check for //@ begin tag
	char* begintag = strstr( line, "//@" );
	if (!begintag) {
		// Nothing of interest to us
		return;
	} else {
		// Skip begin tag
		begintag += 4;
	};

	// set firstdev
	if ( strstr( line, "//@ *** " ) )
	{
		if ( lastdev )
			firstdev = false;
		else
			firstdev = true;
	}
	else
	{
		if ( lastdev )
		{
			char* temp = new char[ strlen( currentchapter->devnotes ) + 6 ];
			sprintf( temp, "%s</UL>", currentchapter->devnotes );
			delete[] currentchapter->devnotes;
			currentchapter->devnotes = temp;
			lastdev = false;
		}
	};

	// huge, ugly case statement, I hate it...
	if ( strstr( line, "//@ DOC " ) )
		ParseDOC( strstr( line, "//@ DOC " ) + 8 );		// Document Text
	else if ( strstr( line, "//@ MAN " ) )
		ParseMAN( strstr( line, "//@ MAN " ) + 8 );		// Manual Title Text
	else if ( strstr( line, "//@ COP " ) )
		ParseCOP( strstr( line, "//@ COP " ) + 8 );		// Copyright Text
	else if ( strstr( line, "//@ SCT " ) )
		ParseSCT( strstr( line, "//@ SCT " ) + 8 );		// Section Title
	else if ( strstr( line, "//@ SEC " ) )
		ParseSEC( strstr( line, "//@ SEC " ) + 8 );		// Section
	else if ( strstr( line, "//@ CHA " ) )
		ParseCHA( strstr( line, "//@ CHA " ) + 8 );		// Chapter
	else if ( strstr( line, "//@ COD " ) )
		ParseCOD( strstr( line, "//@ COD " ) + 8 );		// Chapter Code
	else if ( strstr( line, "//@ NUM " ) )
		ParseNUM( strstr( line, "//@ NUM " ) + 8 );		// Chapter Number
	else if ( strstr( line, "//@ CRI " ) )
		ParseCRI( strstr( line, "//@ CRI " ) + 8 );		// Critical Field
	else if ( strstr( line, "//@ INV " ) )
		ParseINV( strstr( line, "//@ INV " ) + 8 );		// Invalid Field
	else if ( strstr( line, "//@ GEN " ) )
		ParseGEN( strstr( line, "//@ GEN " ) + 8 );		// Chapter General Description Text
	else if ( strstr( line, "//@ TAB " ) )
		ParseTAB( strstr( line, "//@ TAB " ) + 8 );		// Table
	else if ( strstr( line, "//@ SUB " ) )
		ParseSUB( strstr( line, "//@ SUB " ) + 8 );		// Subroutine Call
	else if ( strstr( line, "//@ BUF " ) )
		ParseBUF( strstr( line, "//@ BUF " ) + 8 );		// Buffer
	else if ( strstr( line, "//@ ERR " ) )
		ParseERR( strstr( line, "//@ ERR " ) + 8 );		// Error
	else if ( strstr( line, "//@ INP " ) )
		ParseINP( strstr( line, "//@ INP " ) + 8 );		// Input File
	else if ( strstr( line, "//@ OUT " ) )
		ParseOUT( strstr( line, "//@ OUT " ) + 8 );		// Output File
	else if ( strstr( line, "//@ FIX " ) )
		ParseFIX( strstr( line, "//@ FIX " ) + 8 );		// Fixed-Width Font Text
	else if ( strstr( line, "//@ NOB " ) )
		ParseNOB( strstr( line, "//@ NOB " ) + 8 );		// Non-Bulleted Text
	else if ( strstr( line, "//@ GRS " ) )
		ParseGRS( strstr( line, "//@ GRS " ) + 8 );		// Group Start
	else if ( strstr( line, "//@ GRS" ) )
		ParseGRS( strstr( line, "//@ GRS" ) + 7 );
	else if ( strstr( line, "//@ GRE " ) )
		ParseGRE( strstr( line, "//@ GRE " ) + 8 );		// Group End
	else if ( strstr( line, "//@ GRE" ) )
		ParseGRE( strstr( line, "//@ GRE" ) + 7 );
	else if ( strstr( line, "//@ DBF " ) )
		ParseDBF( strstr( line, "//@ DBF " ) + 8 );		// Database Format
	else if ( strstr( line, "//@ BLD " ) )
		ParseBLD( strstr( line, "//@ BLD " ) + 8 );		// Bolded Text
	else if ( strstr( line, "//@ SVL " ) )
		ParseSYS( strstr( line, "//@ SVL " ) + 8 );		// System Value
	else if ( strstr( line, "//@ DXF " ) )
		ParseDXF( strstr( line, "//@ DXF " ) + 8 );		// Data Exchange Format
	else if ( strstr( line, "//@ VAR " ) )
		ParseVAR( strstr( line, "//@ VAR " ) + 8 );		// Program Variable
	else if ( strstr( line, "//@ *** " ) )
		ParseDEV( strstr( line, "//@ *** " ) + 8 );		// Developer Comment
	else if ( strstr( line, "//@ PAR " ) )
		ParsePAR( strstr( line, "//@ PAR " ) + 8 );		// Function Parameter
	else if ( strstr( line, "//@ UEX " ) ) {
		ParseUEX( strstr( line, "//@ UEX " ) + 8 );		// User Exit
	} else if ( strstr( line, "//@ UTL " ) ) {
		ParseUTL( strstr( line, "//@ UTL " ) + 8 );		// Utility Call
	} else {
		Parse( begintag - 1 );	// catch everything else
	};
};

// Parse a source file a single line at a time, looking for //@ comments
void ParseFile( char* filename )
{
	ifstream fin( filename );
	if ( !fin )
	{
		cerr << "ERROR : Cannot open " << filename << endl;
		return;
	};

	// get source code file name
	if ( devcomments )
	{
		devfile = new char[ strlen( filename ) - strlen( openingdir ) + 1 ];
		strcpy( devfile, filename + strlen( openingdir ) + 1 );
	};

	long i = 0;
	char* fixline = NULL;
	char* currline = new char[ MAX_LINE ];

	fin.getline( currline, MAX_LINE );

	// handle CR/LF vs CR
	if ( currline[ strlen( currline ) - 1 ] == 13 )
		currline[ strlen( currline ) - 1 ] = 0;

	// Reset the defines "stack"
	stack.active = true;

	// While we have data to read:
	while ( !fin.eof() )
	{
		i++;

		// Parse lines containing //@
		if ( strstr( currline, "//@" ) && stack.active)
		{
// Display Lines
//			cout << "   Line # " << i << " : " << currline << endl;
			fixline = FixNice( currline );
			ParseLine( fixline );
			delete [] fixline;
		};

		char* temp;

		// Parse lines containing #ifdef
		temp = strstr( currline, "#ifdef" );
		if (temp) {
			temp += 7;
			stack.IfDef( temp );
		};
		
		// Parse lines containing #ifndef
		temp = strstr( currline, "#ifndef" );
		if (temp) {
			temp += 8;
			stack.IfNDef( temp );
		};

		temp = strstr( currline, "#if " );
		if (temp) {
			temp += 4;
			stack.IfDef( temp );
		};

		// Parse lines containing #else
		temp = strstr( currline, "#else" );
		if (temp) {
			temp += 6;
			stack.Else();
		};

		// Parse lines containing #endif
		temp = strstr( currline, "#endif" );
		if (temp) {
			temp += 7;
			stack.EndIf();
		};

		// Get the next line
		fin.getline( currline, MAX_LINE );

		// handle CR/LF vs CR
		if ( currline[ strlen( currline ) - 1 ] == 13 )
			currline[ strlen( currline ) - 1 ] = 0;
	};

	delete[] currline;
	if ( devfile )
		delete[] devfile;
	fin.close();
};



// Output the html file 
void WriteFile( char* destpath )
{
	chapter* tempchap;
	chapter* searchchap;
	dbtable* temptable;
	dbtable* searchtable;
	dbtable* reftable;
	bool found;
	ofstream fout;

	int start = 1;
	if ( devcomments )
		start = 0;

	// write main file
	fout.open( destpath );

	if ( !fout )
	{
		cerr << "ERROR : Cannot save " << destpath << endl;
		return;
	};
	cout << "Creating " << destpath << endl;

	// header and title page
	fout << "<HTML><HEAD><TITLE>";
	if ( doctitle )
		fout << doctitle;
	fout << "</TITLE><BODY>" << endl;
	fout << "<H5 ALIGN=RIGHT>Generated by The Protea Project SRM Generator/H5>" << endl;
	if ( mantext )
		fout << mantext << endl;
	if ( doctext )
		fout << doctext << endl;
	fout << "<HR SIZE=9>" << endl;
	if ( coptext )
		fout << coptext << endl;
	fout << "<HR SIZE=9>" << endl;

	// table of contents
	fout << "<H2>TABLE OF CONTENTS</H2>" << endl;
	int ctr;
	int i;
	for ( i = start; i < sectitlescount; i++ )
	{
		// show each section in TOC
		fout << "<H3>" << sectoc[ i ] << "</H3><TABLE BORDER=0 ALIGN=CENTER WIDTH='95%'>" << endl;
		tempchap = chapterlist;
		ctr = 0;
		while ( tempchap )
		{
			// show each chapter in current section in TOC
			if ( tempchap->section == i || ( i == 0 && tempchap->devnotes ) )
			{
				if ( !tempchap->code )
					cerr << "ERROR : " << tempchap->name << " missing CODE." << endl;
				char* temp = new char[ strlen( currfile ) + strlen( currext ) + strlen( tempchap->name ) + strlen( tempchap->code ) + 44 ];
				sprintf( temp, "<A HREF='%s_%d.%s#Chapter_%s'>%s</A><BR>", currfile, i, currext, tempchap->code, tempchap->name );
				if ( ctr == 0 )
					fout << "<TR>";
				fout << "<TD>" << temp << "</TD>";
				if ( temp ) delete[] temp;
				ctr++;
				if ( ctr / 3 == 1 )
				{
					fout << "</TR>" << endl;
					ctr = 0;
				};
			}
			tempchap = tempchap->next;
		};
		fout << "</TABLE><BR>" << endl;
	};
	fout << "<HR SIZE=9>" << endl;
	fout << "</BODY></HTML>" << endl;
	fout.close();

	// Create each Section File
	for ( i = start; i < sectitlescount; i++ )
	{
		// write main file
		char* temp = new char[ strlen( currdir ) + strlen( currfile ) + strlen( OSFILEDELIM ) + strlen( currext ) + 10 ];
		sprintf( temp, "%s%s%s_%d.%s", currdir, OSFILEDELIM, currfile, i, currext );
		fout.open( temp );

		if ( !fout )
		{
			cerr << "ERROR : Cannot save " << destpath << endl;
			return;
		};
		cout << "Creating " << temp << endl;

		// header and title page
		fout << "<HTML><HEAD><TITLE>";
		if (secname[i])
			fout << secname[ i ];
		else
			fout << "UNNAMED SECTION";
		fout << " - ";
		if (doctitle)
			fout << doctitle;
		else
			fout << "UNTITLED DOCUMENT";
		fout << "</TITLE><BODY>" << endl;
		fout << "<H5 ALIGN=RIGHT>Generated by The Protea Project SRM Generator</H5>" << endl;

		fout << sectitles[i] << endl;
		tempchap = chapterlist;
		while ( tempchap )
		{
			// Create developer comments in section 0
			if ( i == 0 && tempchap->devnotes )
			{
				fout << "<HR><H3><A NAME='Chapter_" << tempchap->code << "'>" << tempchap->name << "</H3>";
				fout << tempchap->devnotes << endl;
			};

			// Create each Chapter in the current section
			if ( tempchap->section == i )
			{
				// Chapter Title
				fout << "<HR><H3><A NAME='Chapter_" << tempchap->code << "'>" << tempchap->name << "</H3>";

				// Chapter Code
				if ( tempchap->code && tempchap->section != 1 )
					fout << "<TT>Transaction Code : " << tempchap->code << "</TT><BR>" << endl;

				// Chapter Number
				if ( tempchap->number )
					fout << "<TT>Transaction Number : " << tempchap->number << "</TT><BR>" << endl;

				// Source Code Filename
				if ( devcomments && tempchap->sourcefile )
					fout << "<TT><FONT color=red>Source Code File : " << tempchap->sourcefile << "</FONT></TT><BR>" << endl;

				// Subtransaction List
				int subnum = 1;
				if ( tempchap->sublist )
				{
					fout << "<BR><TABLE BORDER=0 WIDTH='100%'><TR><TD><TT>Subtransactions :</TT></TD></TR><TR><TD><TT>-----------------</TT></TD></TR>" << endl;
					temptable = tempchap->sublist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>";
						// check for existing subtransaction...
						found = false;
						searchchap = chapterlist;
						while ( searchchap && !found )
						{
							if ( searchchap->section == 3 )
							{
								// it exists, print link...
								if ( ! XCStrCaseCompare( searchchap->code, temptable->table ) )
								{
									fout << "<TT><A HREF='" << currfile << "_3." << currext << "#Chapter_" << searchchap->code << "'>" << searchchap->name << "</A><SUP>" << subnum << "</SUP></TT><BR>" << endl;
									
									// if self, don't continue down the list
									if ( XCStrCaseCompare( searchchap->code, tempchap->code ) )
									{
										// add tables to tablelist with superscript
										searchtable = searchchap->tablelist;
										while ( searchtable )
										{
											// Create the new record
											dbtable* newtable = new dbtable(
												searchtable->table,
												"S",
												searchtable->access,
												"<SUP>%d</SUP>%s", subnum, searchtable->link );

											// Find where to put it
											reftable = tempchap->tablelist;
											if ( reftable )
											{
												while ( reftable->next )
													reftable = reftable->next;
												reftable->next = newtable;
												reftable = reftable->next;
											}
											else
											{
												reftable = newtable;
												tempchap->tablelist = reftable;
											};
											searchtable = searchtable->next;
										};
/*
										// add errors to errorlist with superscript
										searchtable = searchchap->errorlist;
										while ( searchtable )
										{
											reftable = tempchap->errorlist;
											if ( reftable )
											{
												while ( reftable->next )
													reftable = reftable->next;
												reftable->next = new dbtable();
												reftable = reftable->next;
											}
											else
											{
												reftable = new dbtable();
												tempchap->errorlist = reftable;
											};
											reftable->iotype = new char[ strlen( searchtable->iotype ) + 20 ];
											sprintf( reftable->iotype, "<SUP>%ld</SUP>%s", subnum, searchtable->iotype );
											reftable->link = strdup( searchtable->link );
											searchtable = searchtable->next;
										};
*/
										found = true;
										subnum++;
									};
								};
							};
							searchchap = searchchap->next;
						};

						// it doesn't exist, print name...
						if ( !found )
						{
							fout << temptable->table;
							cout << "WARNING: " << temptable->table << " Subtransaction Link Not Found for " << tempchap->name << endl;
						};
						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
					fout << "</TABLE>" << endl;
				};

				// Utility List
				if ( tempchap->utillist )
				{
					fout << "<BR><TABLE BORDER=0 WIDTH='100%'><TR><TD><TT>Utilities :</TT></TD></TR><TR><TD><TT>----------</TT></TD></TR>" << endl;
					temptable = tempchap->utillist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>";
						// check for existing utility...
						found = false;
						searchchap = chapterlist;
						while ( searchchap && !found )
						{
							if ( searchchap->section == 5 )
							{
								// it exists, print link...
								if ( ! XCStrCaseCompare( searchchap->code, temptable->table ) )
								{
									fout << "<TT><A HREF='" << currfile << "_5." << currext << "#Chapter_" << searchchap->code << "'>" << searchchap->name << "</A><SUP>" << subnum << "</SUP></TT><BR>" << endl;
								
									// if self, don't continue down the list
									if ( XCStrCaseCompare( searchchap->code, tempchap->code ) )
									{
										// Add tables to tablelist with superscript
										searchtable = searchchap->tablelist;
										while ( searchtable )
										{
											// Create the new record
											dbtable* newtable = new dbtable(
												searchtable->table,
												"S",
												searchtable->access,
												"<SUP>%d</SUP>%s", subnum, searchtable->link );

											// Add it to the list
											reftable = tempchap->tablelist;
											if ( reftable )
											{
												while ( reftable->next )
													reftable = reftable->next;
												reftable->next = newtable;
												reftable = reftable->next;
											}
											else
											{
												reftable = newtable;
												tempchap->tablelist = reftable;
											};
											searchtable = searchtable->next;
										};

										// add errors to errorlist with superscript
										searchtable = searchchap->errorlist;
										while ( searchtable )
										{
											char* iotype = new char[ strlen( searchtable->iotype ) + 25 ];
											sprintf( iotype, "<SUP>%d</SUP>%s", subnum, searchtable->iotype );

											dbtable* newtable = new dbtable(
												NULL,
												iotype,
												NULL,
												searchtable->link );

											delete [] iotype;

											reftable = tempchap->errorlist;
											if ( reftable )
											{
												while ( reftable->next )
													reftable = reftable->next;
												reftable->next = newtable;
												reftable = reftable->next;
											}
											else
											{
												reftable = newtable;
												tempchap->errorlist = reftable;
											};
											searchtable = searchtable->next;
										};

										found = true;
										subnum++;
									}
									else
										found = true;
								};
							};
							searchchap = searchchap->next;
						}; // while loop

						// it doesn't exist, print name...
						if ( !found )
						{
							fout << temptable->table;
							cout << "WARNING: " << temptable->table << " Utility Link Not Found for " << tempchap->name << endl;
						};
						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					}; // while temptable
					fout << "</TABLE>" << endl;
				};

				// Critical Field List
				if ( tempchap->critical )
					fout << "<BR>" << tempchap->critical << "</TT><BR>" << endl;

				// Invalid Field List
				if ( tempchap->invalid )
					fout << "<BR>" << tempchap->invalid << "</TT><BR>" << endl;

				// System Value List
				if ( tempchap->sysvals )
					fout << "<BR>" << tempchap->sysvals << "</TT><BR>" << endl;

				// Data Exchange Format List
				if ( tempchap->dxf )
					fout << "<BR>" << tempchap->dxf << "</TT><BR>" << endl;

				// Input File List
				if ( tempchap->inputfile )
					fout << "<BR>" << tempchap->inputfile << endl;

				// Output File List
				if ( tempchap->outputfile )
					fout << "<BR>" << tempchap->outputfile << endl;

				// Table List
				if ( tempchap->tablelist )
				{
					fout << "<BR><TABLE BORDER=0 WIDTH='40%'><TR><TD><TT>Database Tables :</TT></TD><TD><TT>Access : </TT></TD><TD><TT>I/O Type :</TT></TD></TR><TR><TD><TT>-----------------</TT></TD><TD><TT>--------</TT></TD><TD><TT>----------</TT></TD></TR>" << endl;
					temptable = tempchap->tablelist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>";

						// check for existing table...
						found = false;
						searchchap = chapterlist;
						while ( searchchap && !found )
						{
							if ( searchchap->section == 1 )
							{
								// it exists, print link...
								if ( ! XCStrCaseCompare( searchchap->name, temptable->table ) )
								{
									fout << temptable->link;
									found = true;
								};
							};
							searchchap = searchchap->next;
						};

						// it doesn't exist, print name...
						if ( !found )
						{
							fout << temptable->table;
							cout << "WARNING: " << temptable->table << " Table Link Not Found for " << tempchap->name << endl;
						};

						fout << "</TT></TD><TD><TT>" << temptable->access << "</TT></TD><TD><TT>";
						if ( strcmp( temptable->iotype, "I" ) == 0 )
							fout << "Input";
						else
						if ( strcmp( temptable->iotype, "O" ) == 0 )
							fout << "Output";
						else
						if ( strcmp( temptable->iotype, "IO" ) == 0 )
							fout << "Input/Output";
						else
						if ( strcmp( temptable->iotype, "N" ) == 0 )
							fout << "None";
						else
						if ( strcmp( temptable->iotype, "S" ) == 0 )
							fout << "Subtransaction";
						else
							fout << "<FONT color=red>!!! MISSING !!!</FONT>";
						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
					fout << "</TABLE>" << endl;
				};

				// Buffer List
				if ( tempchap->section != 1 )
					fout << "<BR><TABLE BORDER=0 WIDTH='40%'><TR><TD><TT>Data Buffers :</TT></TD><TD><TT>I/O Type :</TT></TD></TR><TR><TD><TT>-----------------</TT></TD><TD><TT>----------</TT></TD></TR>" << endl;
				if ( tempchap->section == 2 )
					fout << "<TR><TD><TT>INPUT</TT></TD><TD><TT>Input/Output</TT></TD></TR>" << endl;

				// Show Buffers for Tables
				if ( tempchap->tablelist )
				{
					temptable = tempchap->tablelist;
					while ( temptable && temptable->iotype[0] != 'S' )
					{
						fout << "<TR><TD><TT>";
						fout << temptable->table;
						fout << "</TT></TD><TD><TT>";
						if ( strcmp( temptable->iotype, "I" ) == 0 )
							fout << "Input";
						else
						if ( strcmp( temptable->iotype, "O" ) == 0 )
							fout << "Output";
						else
						if ( strcmp( temptable->iotype, "IO" ) == 0 )
							fout << "Input/Output";
						else
						if ( strcmp( temptable->iotype, "N" ) == 0 )
							fout << "None";
						else
							fout << "<FONT color=red>!!! MISSING !!!</FONT>";
						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
				};

				// Show Buffer List
				if ( tempchap->bufferlist )
				{
					temptable = tempchap->bufferlist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>";

						// check for existing table...
						found = false;
						searchchap = chapterlist;
						while ( searchchap && !found )
						{
							if ( searchchap->section == 6 )
							{
								// it exists, print link...
								if ( ! XCStrCaseCompare( searchchap->name, temptable->table ) )
								{
									fout << temptable->link;
									found = true;
								};
							};
							searchchap = searchchap->next;
						};

						// it doesn't exist, print name...
						if ( !found )
						{
							fout << temptable->table;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//							cout << "WARNING: " << temptable->table << " Buffer Link Not Found for " << tempchap->name << endl;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
						};

						fout << "</TT></TD><TD><TT>";
						if ( temptable->iotype )
						{
							if ( strcmp( temptable->iotype, "I" ) == 0 )
								fout << "Input";
							else
							if ( strcmp( temptable->iotype, "O" ) == 0 )
								fout << "Output";
							else
							if ( strcmp( temptable->iotype, "IO" ) == 0 )
								fout << "Input/Output";
							else
							if ( strcmp( temptable->iotype, "N" ) == 0 )
								fout << "None";
							else
								fout << "<FONT color=red>!!! MISSING !!!</FONT>";
						}
						else
							fout << "<FONT color=red>!!! MISSING !!!</FONT>";

						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
				};
				fout << "</TABLE>" << endl;

				// Parameter List
				if ( tempchap->parlist )
				{
					fout << "<BR><TABLE BORDER=0 WIDTH='40%'><TR><TD><TT>Parameters :</TT></TD><TD><TT>I/O Type :</TT></TD></TR><TR><TD><TT>------------</TT></TD><TD><TT>----------</TT></TD></TR>" << endl;
					temptable = tempchap->parlist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>";

						// check for existing table...
						found = false;
						searchchap = chapterlist;
						while ( searchchap && !found )
						{
							if ( searchchap->section == 6 )
							{
								// it exists, print link...
								if ( ! XCStrCaseCompare( searchchap->name, temptable->table ) )
								{
									fout << temptable->link;
									found = true;
								};
							};
							searchchap = searchchap->next;
						};

						// it doesn't exist, print name...
						if ( !found )
						{
							fout << temptable->table;
							cout << "WARNING: " << temptable->table << " Parameter Link Not Found for " << tempchap->name << endl;
						};

						fout << "</TT></TD><TD><TT>";
						if ( temptable->iotype )
						{
							if ( strcmp( temptable->iotype, "I" ) == 0 )
								fout << "Input";
							else
							if ( strcmp( temptable->iotype, "O" ) == 0 )
								fout << "Output";
							else
							if ( strcmp( temptable->iotype, "IO" ) == 0 )
								fout << "Input/Output";
							else
							if ( strcmp( temptable->iotype, "N" ) == 0 )
								fout << "None";
							else
								fout << "<FONT color=red>!!! MISSING !!!</FONT>";
						}
						else
							fout << "<FONT color=red>!!! MISSING !!!</FONT>";
						fout << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
					fout << "</TABLE>" << endl;
				};

				// User Exit List
				if ( tempchap->uex )
					fout << "<BR>" << tempchap->uex << endl;

				// Error List
				if ( tempchap->errorlist )
				{
					fout << "<BR><TABLE BORDER=0 WIDTH='100%'><TR><TD><TT>Errors :</TT></TD><TD></TD></TR><TR><TD><TT>--------</TT></TD></TR>" << endl;
					temptable = tempchap->errorlist;
					while ( temptable )
					{
						fout << "<TR><TD><TT>" << temptable->iotype << "</TT></TD>";
						fout << "<TD><TT>" << temptable->link << "</TT></TD></TR>" << endl;
						temptable = temptable->next;
					};
					fout << "</TABLE>" << endl;
				};

				// Variable List
				if ( tempchap->vars )
					fout << "<BR>" << tempchap->vars << endl;

				// Chapter General Description
				if ( tempchap->general )
					fout << tempchap->general << endl;

				// Chapter Detailed Description
				if ( tempchap->detail )
					fout << tempchap->detail << endl;

				// Database Formats
				if ( tempchap->dbf )
					fout << tempchap->dbf << "</TABLE>" << endl;

				// Where Used List of transactions, subtransactions and utilities
				if ( tempchap->section == 1 || tempchap->section == 3 || tempchap->section == 5 )
				{
					ctr = 0;
					fout << "<BR><TABLE BORDER=0 WIDTH='95%'><TR><TD><TT>Where Used :</TT></TD></TR><TR><TD><TT>------------</TT></TD></TR>" << endl;
					searchchap = chapterlist;
					while ( searchchap )
					{
						if ( tempchap->section == 1 )
							searchtable = searchchap->tablelist;
						else
						if ( tempchap->section == 3 )
							searchtable = searchchap->sublist;
						else
						if ( tempchap->section == 5 )
							searchtable = searchchap->utillist;
						while ( searchtable )
						{
							if ( ! XCStrCaseCompare( searchtable->table, tempchap->code ) )
							{
								if ( ctr == 0 )
									fout << "<TR>";
								fout << "<TD><TT><A HREF='" << currfile << "_" << searchchap->section << "." << currext << "#Chapter_" << searchchap->code << "'>" << searchchap->name << "</A></TT></TD>" << endl;
								ctr++;
								if ( ctr / 3 == 1 )
								{
									fout << "</TR>" << endl;
									ctr = 0;
								};
							};
							searchtable = searchtable->next;
						};
						searchchap = searchchap->next;
					};
					fout << "</TABLE>" << endl;


					// TODO: Temporary fix
					fout << "</UL></UL></UL>" << endl;
				};
			};
			tempchap = tempchap->next;
		};
		fout << "</BODY></HTML>" << endl;
		fout.close();

		delete[] temp;
	}
};

void ParseDirectory( char* directory, char* destpath )
{
	char* filename = new char[ MAX_PATH ];
	char* fullfile = new char[ MAX_PATH ];
	bool found = true;
	DIR_HANDLE_TYPE srcdir;

	// iterate through all of the files in the directory, parsing only .h and .cpp
	try {
		srcdir = XCDirFirstFile( directory, filename );
	} catch (...) {
		// most likely (we hope) you tried to open a file as a directory, so it'll fail...
		cout << "Skipping file : " << directory << endl;
		return;
	};
	while ( found )
	{
		sprintf( fullfile, "%s%s%s", directory, OSFILEDELIM, filename );
		if ( strstr( filename, ".cpp" ) || strstr( filename, ".h" ) )
		{
			if (filename[0] == '.' && filename[1] == '#')
			{
				cout << "Skipping CVS backup file : " << fullfile << endl;
			}
			else
			{
				cout << "Parsing file : " << fullfile << endl;
				ParseFile( fullfile );
			};
		}
		else
		{
			if ( strcmp( filename, "." ) != 0 && strcmp( filename, ".." ) != 0 && strcmp( filename, "CVS" ) != 0)
			{
				// Call recursively, parse all child directories
				ParseDirectory( fullfile, destpath );
			}
			else
			{
				// But don't try to call it again on . or .. they will cause endless loops (fun!)
				cout << "Skipping file : " << fullfile << endl;
			};
		};
		found = XCDirNextFile( srcdir, filename );
	};
	XCDirClose( srcdir );

	// Okay, we can collect the garbage now...
	if ( filename )
		delete[] filename;
	if ( fullfile )
		delete[] fullfile;

};

// Main for SRM
int main(int argc, char* argv[]) 
{
	char* delim = NULL;


	char* dir_parm = NULL;
	char* dest_file = NULL;

	bool bad = false;
	bool valid = false;

	Protea::ShowIntro( "srm" );

	if (argc > 1 && !strcmp(argv[1],"-L"))
	{
		Protea::ShowLicense();
		return 0;
	};

	for (int i=1; i<argc; i++) {
		if (argv[i][0] == '-') {
			if (strcmp( "-NODEV", argv[i] )==0) {
				devcomments = false;
			} else if (strlen( argv[i] ) > 1) {
				if (argv[i][1] == 'D') {
					stack.AddDefine( &(argv[i][2]) );
				} else
					bad = true;
			};
		} else if (!dir_parm) {
			dir_parm = argv[i];
		} else if (!dest_file) {
			dest_file = argv[i];
			valid = true;
		} else
			bad = true;
	};

	// get directory
	if (!valid || bad)
	{
		cout << "Usage: srm <directory> <dest file>" << endl;
		return 1;
	};

	Protea::Initialize();
	try {
		DO_DEVTOOL_SECURITY_CHECK;
	} catch( XCToolsError& error ) {
		printf( "Error initializing: %s\n", error.GetDescription() );
		Protea::Uninitialize();
		return 1;
	};

	currdir = NULL;
	currfile = NULL;
	currext = NULL;
	doctext = NULL;
	doctitle = NULL;
	mantext = NULL;
	coptext = NULL;
	sectitles = NULL;
	sectitlescount = 0;
	sectoc = NULL;
	secname = NULL;
	currentsection = 0;
	chapterlist = NULL;
	currentchapter = NULL;

	// get filename portion of destpath / extension portion
	delim = new char[ strlen( OSFILEDELIM ) + 1 ];
	strcpy( delim, OSFILEDELIM );

	long slen = strlen( dest_file ) + 1;
	currdir = new char[ slen ];
	memset( currdir, 0, slen );
	char* tempstart = strrchr( dest_file, delim[0] );
	if (tempstart) {
		tempstart++;
		strncpy( currdir, dest_file, tempstart - dest_file );
	} else
		strcpy( currdir, "." );
cout << "currdir:" << currdir << endl;

	tempstart = strrchr( dest_file, (int)'.' ) + 1;
	currext = new char[ strlen( tempstart ) + 1 ]; 
	strcpy( currext, tempstart );
cout << "currext:" << currext << endl;

	tempstart = strrchr( dest_file, delim[0] );
	if (tempstart) {
		tempstart++;
		slen = strlen( tempstart ) - strlen( currext ) - 1;
	} else
		slen = strlen( dest_file );

	currfile = new char[ slen + 1 ];
	memset( currfile, 0, slen + 1 );

	if (tempstart)
		strncpy( currfile, tempstart, slen );
	else
		strcpy( currfile, dest_file );

cout << "currfile:" << currfile << endl;

	// set up developer comments
	char* temp = new char[ 22 ];
	strcpy( temp, "0 Developer Comments" );
	ParseSCT( temp );
	delete[] temp;

	openingdir = new char[ strlen( dir_parm ) + 1 ];
	strcpy( openingdir, dir_parm );
	ParseDirectory( openingdir, dest_file );

	// Write the file now...
	WriteFile( dest_file );

	// clean up...
	delete[] delim;
	delete[] currdir;
	delete[] currfile;
	delete[] currext;
	delete[] openingdir;
	if ( doctext )
		delete[] doctext;
	if ( doctitle )
		delete[] doctitle;
	if ( mantext )
		delete[] mantext;
	if ( coptext )
		delete[] coptext;

	// clean up sections
	for ( int i = 0; i < sectitlescount; i++ )
	{
		if ( sectitles[ i ] )
			delete[] sectitles[ i ];
		if ( sectoc[ i ] )
			delete[] sectoc[ i ];
		if ( secname[ i ] )
			delete[] secname[ i ];
	};
	if ( sectitles )
		delete[] sectitles;
	if ( sectoc )
		delete[] sectoc;
	if ( secname )
		delete[] secname;

	// clean up chapters
	chapter* tempchap;
	while ( chapterlist )
	{
		tempchap = chapterlist;
		chapterlist = chapterlist->next;

		printf( "DELETE CHAPTER: %s\n", tempchap->name );
		delete tempchap;
	};

	cout << "SRM program finished." << endl;
//	int lmnop;
//	cin >> lmnop;
	return 0;
};

